/*
 * # iohao.com . 渔民小镇
 * Copyright (C) 2021 - 2022 double joker （262610965@qq.com） . All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.iohao.game.simulation.client.data;

import com.iohao.game.action.skeleton.core.*;
import com.iohao.game.action.skeleton.core.doc.ActionCommandDoc;
import com.iohao.game.action.skeleton.core.doc.BarSkeletonDoc;
import com.iohao.game.action.skeleton.core.doc.JavaClassDocInfo;
import com.iohao.game.simulation.client.data.dto.ActionControllerDto;
import com.iohao.game.simulation.client.data.dto.ActionDto;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
import org.jctools.maps.NonBlockingHashMap;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * @author 渔民小镇
 * @date 2022-11-01
 */
@Getter
@Setter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActionDataManager {
    /**
     * action类 与 action类方法的映射
     * <pre>
     *     key : cmd
     *     value: subCmd list
     * </pre>
     */
    Map<Integer, List<ActionDto>> actionDtoMap = new HashMap<>();

    /** action controller info list */
    List<ActionControllerDto> actionControllerDtoList = new ArrayList<>();

    /**
     * cmd 路由对应的入参数据类型信息
     * <pre>
     *     key : cmdMerge
     *     value : 路由入参对应的 class 信息
     *
     *     开发阶段的数据辅助信息，目前主要提供给"模拟客户端" 时使用的。
     *
     *     此 map 中，保存了：
     *     1 action 的返回值类信息；
     *     2 入参时的类信息；
     *
     * </pre>
     */
    Map<Integer, Class<?>> actionInputClassMap = new NonBlockingHashMap<>();

    /**
     * 初始化模拟客户端端数据
     */
    public void init() {

        // action类、action 信息
        var actionControllerInfoList = this.streamActionCommandRegion().map(actionCommandRegion -> {
            JavaClassDocInfo javaClassDocInfo = actionCommandRegion.getJavaClassDocInfo();
            Class<?> actionControllerClazz = actionCommandRegion.getActionControllerClazz();

            var actionControllerInfo = new ActionControllerInfo();
            actionControllerInfo.setCmd(actionCommandRegion.getCmd())
                    .setActionSimpleName(actionControllerClazz.getSimpleName())
                    .setComment(javaClassDocInfo.getComment());

            // 得到 actionController 下的所有 action
            List<ActionInfo> actionInfos = this.listActionInfo(actionCommandRegion);
            actionControllerInfo.setActionInfoList(actionInfos);

            return actionControllerInfo;
        }).toList();

        // action类、action 信息 -- DTO
        actionControllerInfoList.forEach(actionControllerInfo -> {
            ActionControllerDto dto = actionControllerInfo.toDto();
            this.actionControllerDtoList.add(dto);

            List<ActionDto> actionDtoList = actionControllerInfo
                    .getActionInfoList()
                    .stream()
                    .map(ActionInfo::toDto)
                    .toList();

            int cmd = actionControllerInfo.getCmd();
            this.actionDtoMap.put(cmd, actionDtoList);
        });

    }

    private List<ActionInfo> listActionInfo(ActionCommandRegion actionCommandRegion) {
        return actionCommandRegion.values().stream().map(actionCommand -> {

            extractedActionInputParam(actionCommand);

            CmdInfo cmdInfo = actionCommand.getCmdInfo();
            ActionCommandDoc actionCommandDoc = actionCommand.getActionCommandDoc();
            ActionCommand.ActionMethodReturnInfo actionMethodReturnInfo = actionCommand.getActionMethodReturnInfo();
            var paramInfo = this.getInputParamInfo(actionCommand.getParamInfos());

            ActionInfo actionInfo = new ActionInfo()
                    .setCmd(cmdInfo.getCmd())
                    .setSubCmd(cmdInfo.getSubCmd())
                    .setComment(actionCommandDoc.getComment())
                    .setMethodName(actionCommand.getActionMethodName())
                    .setReturnTypeClazzName(actionMethodReturnInfo.getReturnTypeClazzName())
                    .setLineNumber(actionCommandDoc.getLineNumber())
                    .setActionCommand(actionCommand)
                    .setParamInfo(paramInfo)
                    .setActionMethodReturnInfo(actionMethodReturnInfo)
                    .setActionControllerClassName(actionCommandRegion.getActionControllerClazz().getName());

            if (paramInfo != null) {
                actionInfo.setParamName(paramInfo.getName())
                        .setParamClazzName(paramInfo.getMethodParamClassName());
            }


            return actionInfo;
        }).toList();
    }

    private ActionCommand.ParamInfo getInputParamInfo(ActionCommand.ParamInfo[] paramInfos) {

        for (ActionCommand.ParamInfo paramInfo : paramInfos) {
            if (paramInfo.isExtension()) {
                continue;
            }

            return paramInfo;
        }

        return null;
    }

    private Stream<ActionCommandRegion> streamActionCommandRegion() {
        return BarSkeletonDoc.me().listBarSkeleton().parallelStream()
                // 得到业务框架的 ActionCommandRegions
                .map(BarSkeleton::getActionCommandRegions)
                // 将 map.values 合并成一个 list，即将 ActionCommandRegions 中的 regionMap 的 value 转为 stream
                .flatMap((Function<ActionCommandRegions, Stream<ActionCommandRegion>>) actionCommandRegions -> actionCommandRegions.getRegionMap().values().parallelStream());
    }

    private void extractedActionInputParam(ActionCommand actionCommand) {
        ActionCommand.ParamInfo[] paramInfos = actionCommand.getParamInfos();

        for (ActionCommand.ParamInfo paramInfo : paramInfos) {
            if (paramInfo.isExtension()) {
                continue;
            }

            Class<?> paramClazz = paramInfo.getParamClazz();
            int cmdMerge = actionCommand.getCmdInfo().getCmdMerge();
            this.actionInputClassMap.putIfAbsent(cmdMerge, paramClazz);
        }
    }

    private ActionDataManager() {

    }

    public static ActionDataManager me() {
        return Holder.ME;
    }

    /** 通过 JVM 的类加载机制, 保证只加载一次 (singleton) */
    private static class Holder {
        static final ActionDataManager ME = new ActionDataManager();
    }
}
